package com.company.config;


import com.company.aop.LogAspect;
import com.company.aop.MathCalculator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;


/*
AOP:将业务逻辑组件和切面类都加入到容器中，告诉spring那个是切面类
    在切面类上的每一个通知方法标注通知注释，告诉spring何时何地运行
    开启基于注释的aop模式，@EnableAspectJAutoProxy

AOP的原理：
    @EnableAspectJAutoProxy是什么
    @Import({AspectJAutoProxyRegistrar.class})给容器中导入
    给容器创建一个AnnotationAwareAspectJAutoProxyCreator

    AnnotationAwareAspectJAutoProxyCreator最终继承SmartInstantiationAwareBeanPostProcessor

    AbstractAutoProxyCreator.setBeanFactory()
    AbstractAutoProxyCreator有后置处理器 postProcessorBefore

    AbstractAdvisorAutoProxyCreator.setBeanFactory()里面调用了initBeanFactory


    AnnotationAwareAspectJAutoProxyCreator.initBeanFactory


    流程:1传入配置类，创建ioc容器
   2 注册配置类，调用refresh（）方法刷新容器
   3 registerBeanPostProcessors（beanFactory）注册bean的后置处理器来方便拦截bean的创建。
    1：先获取ioc容器中已经定义了需要创建对象的所有BeanPostProcessor
    2：给容器中加别的BeanPostProcessor
    3：优先注册实现了PriorityOrder接口的BeanPostProcessor;
    4:再给容器中注册实现了Ordered接口的BeanPostProcessor
    5：注册没实现优先级接口的BeanPostProcessor
    6：注册BeanPostProcessor，实际上就是创建BeanPostProcessor对象，保存在容器中
       创建internalAutoProxyCreator的BeanPostProcessor
         1:创建Bean的实例
         2：populateBean给Bean的各种2属性赋值
         3：initializeBean 初始化Bean
              1：invokeAwareMethods(),出力aware接口方法回调
              2：applyBeanPostprocessorBeforeInitialization(),应用这个后置处理器的BeforeInitialization()
              3：invokeInitMethods（）执行自定义的初始化方法
              4: applyBeanPostprocessorAfterInitialization()执行后置处理器的AfterInitialization
         4:BeanPostProcessor（AnnotationAwareAspectJAutoProxyCreator）创建成功
    7：把BeanPostProcessor注册到BeanFactory中 beanFactory.addBeanPostProcessor(postProcessor);
  4 finishBeanFactoryInitialization(beanFactory)完成BeanFactory的初始化
       1：遍历容器中所有的Bean，依次创建对象getBean（beanName)
           getBean->doGetBean->getSingleton
       2:创建Bean
        AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截， InstantiationAwareBeanPostProcessor，
        会调用postProcessBeforeInstantiation（）
           1：先从缓存中获取当前Bean，如果能获取到说明bean是之前被创建过的，直接使用，否则再创建
              只要创建Bean都会被缓存起来
           2;createBean（）；创建bean， AnnotationAwareAspectJAutoProxyCreator会在任何bean创建之前先尝试返回bean实例
           BeanPostProcessor是在Bean对象创建完成初始化前后调用的
           InstantiationAwareBeanPostProcessor是在创建Bean实例化之前先尝试用后置处理器返回对象的
             1:resolveBeforeInstantiation(beanName,mbdToUse)解释BeforeInstantiation
                1后置处理器尝试返回对象
                  bean = applyBeanPostProcessorBeforeInstantiation（）
                  拿到所有后置处理器，如果是InstantiationAwareBeanPostProcessor；就执行postProcessBeforeInstantiation
                    if（bean != null）{
                       bean = applyBeanPostProcessorAfterInitialization(bean,beanName)
                    }
             希望后置处理器在此返回一个代理对象，如能能返回就使用，如果不能
             2：doCreateBean（beanName，mbdToUse，args），

-------------------------------------------------------------------------------------------------------------------------------

    AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】的作用
    1：每一个bean创建之前，调用postProcessorBeforeInstantiation
        关心MathCalculator和LogAspect
         1：判断当前bean是否在advisedBean中（保存了所有需要增强的bean）
         2：判断当前bean是否是基础类型的Advice，Pointcut Advisor，AopInfrastructureBean或者是否是切面
         3：判断是否需要跳过
            1：获取候选的增强器（切面的通知方法）【List<Advisor> candidateAdvisors】\
            每一个封装的通知方法的增强器是InstantiationModelAwarePointcutAdvisor
            判断每一个增强器是否是AspectJPointcutAdvisor类型的，返回true
            2：永远返回false
    2：创建对象
    postProcessorAfterInitialization
    return wrapIfNecessary（bean，beanName，cacheKey）包装如果需要的情况下
      1:获取当前bean使用的增强器（通知方法:Object[] specificInterceptors
         1找到能在当前bean使用的增强器（找哪些通知方法是需要切入当前bean方法的）
         2：获取到能在bean使用的增强器
         3：给增强器排序
      2：保存当前bean在adviseBean中
      3：如果当前bean需要增强，创建当前bean的代理对象
         1：获取所有增强器（通知方法）
         2:保存到proxyFactory
         3：创建代理
            spring自动决定
            JdkDynamicAopProxy（config）；jdk动态代理
            ObjenesisCglibAopProxy（config）；cglib动态代理
      4：给容器中返回当前组件使用的cglib增强了的代理对象
      5：以后容器中获取到就是这个组件的代理对象，执行目标方法的时候，代理对象就会执行通知方法的流程
   3目标方法执行
      容器中保存了组件的代理对象（增强后的对象）这个对象里面保存了详细信息（比如增强器，目标对象）
        1：CglibAopProxy.intercept();拦截目标方法执行
        2：根据proxyFactory获取目标方法将要执行的拦截器链
           List<Object>chain = this.advised
           .getInterceptorsAndDynamicInterceptionAdvice(method,targetClass)
              1：List<Object> interceptorList保存所有的拦截器5
               一个默认的ExposeInvocationInterceptor和4个增强器
              2：遍历所有的增强器，将其转为Interceptor
                 registry.getInterceptors(advisor);
              3:将增强器转为List<MethodInterceptor>;
                 如果是MethodInterceptor 直接加入到集合
                 如果不是，使用AdvisorAdapter将增强器转为MethodInterceptor
        3:如果没有拦截器链，直接执行目标方法
          拦截器链（每一个通知方法又被包装成方法拦截器，利用MethodInterceptor机制）
        4：如果有，把需要执行的的目标对象，目标方法，拦截器链等信息传入到一个CglibMethodInvocation对象
        并调用proceed（）方法
        5：拦截器链的触发过程（责任链模式）
        currentlInterceptorIndex 记录当前索引，每次执行proceed，索引自增一次
           1 ：如果没有拦截器执行目标方法，或者拦截器的索引和拦截器数组-1大小一样（指定到最后一个拦截器）执行目标方法
           2  链式获取每一个拦截器，拦截器执行invoke方法，每一个拦截器等待下一个拦截器执行完成返回后再来执行
              拦截器机制，保证保证顺序

  总结
       1：@EnableAspectJAutoProxy  开启aop功能
       2：@EnableAspectJAutoProxy 会给容器注册一个组件AnnotationAwareAspectJAutoProxyCreator
       3：AnnotationAwareAspectJAutoProxyCreator是一个后置处理器
       4: 容器的创建流程：
           1：registerBeanPostProcessor（）注册后置处理器，创建AnnotationAwareAspectJAutoProxyCreator的对象
           2：finishBeanFactoryInitialization（）初始化剩下的单实例bean
              1：创建业务逻辑组件和切面组件
              2：AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
              3;组件创造完成，判断组件是否需要增强
                 是：切面的通知方法，包装成增强器（Advisor）给业务逻辑组件创建一个代理对象（cglib）
       5：执行目标方法
           1:代理对象执行目标方法
           2：CglibAopProxy.intercept()
               1:得到目标方法的拦截器链（增强器包装秤拦截器MethodInterceptor）
               2：利用拦截器的链式机制，一次进入每一个拦截器进行执行
               3：效果
                   正常执行  前置通知-》目标方法-》后置通知-》返回通知
                   异常执行  前置通知-》目标方法-》猴纸通知-》异常通知


 */
@Configuration
@EnableAspectJAutoProxy //开启基于注解的aop模式
public class MainConfig {

    @Bean
    public MathCalculator calculator() {
        return new MathCalculator();
    }

    @Bean
    public LogAspect logAspect() {
        return new LogAspect();
    }

}
